package com.wd.cloud.applet.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wd.cloud.applet.common.exception.CustomException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * Description:常用工具类
 *
 * @version 1.0
 */
public class HelperUtils {
    public static boolean isNullOrTrimEmpty(Object o) {
        return o == null || "".equals(o.toString().trim());
    }
    public static boolean isNullEmpty(Object o) {
        return o == null;
    }

    public static String toString(Object o) {
        if (isNullOrTrimEmpty(o)) {
            return null;
        }
        return o.toString();
    }

    public static Integer toInt(Object o) {
        if (isNullOrTrimEmpty(o)) {
            return null;
        }
        return Integer.valueOf(o.toString());
    }

    public static Long toLong(Object o) {
        if (isNullOrTrimEmpty(o)) {
            return null;
        }
        return Long.valueOf(o.toString());
    }

    public static Double toDouble(Object o) {
        if (isNullOrTrimEmpty(o)) {
            return null;
        }
        return Double.valueOf(o.toString());
    }
    /**
     * 驼峰格式字符串转换为指定格式分割字符串
     *
     * @param str       要分隔的驼峰格式字符串
     * @param partition 分割符
     * @return 分割好的字符串
     */
    public static String humpToPartition(String str, String partition) {
        if (str == null || "".equals(str.trim())) {
            return null;
        }
        int len = str.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = str.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(partition);
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }


    public static <T> void notNull(T var1, String name, Integer code) {
        if (var1 == null || var1.toString().trim().length() == 0) {
            throw new CustomException(name + "不能为空", code);
        }
    }

    public static <T> void notNull(T var1, String name) {
        notNull(var1, name, 100);
    }

    public static void notNegative(Number number, String name) {
        notNull(number, name);
        if (new BigDecimal(number.toString()).compareTo(BigDecimal.ZERO) < 1) {
            throw new CustomException(name + "必须大于0");
        }
    }


    public static <T> T defaultValue(T var1, T defValue) {
        if (var1 != null) {
            return var1;
        }
        return defValue;
    }

    public static double decimal() {
        double dou = 0.01;
        return dou;
    }

    /**
     * 精确的乘法运算
     *
     * @param v1
     * @param v2
     * @return
     */
    public static Integer multiply(double v1, double v2) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return b1.multiply(b2).intValue();
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时
     * 精确到小数点后10位的数字四舍五入
     *
     * @param v1
     * @param v2
     * @return
     */
    public static Integer divide(Integer v1, double v2) {
        BigDecimal b1 = BigDecimal.valueOf(v1);
        BigDecimal b2 = BigDecimal.valueOf(v2);
        return b1.divide(b2, BigDecimal.ROUND_HALF_UP).intValue();
    }

    public static Date getDateByLocalDateTime(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    public static String getLoginUserIdNull(Map params) {
        if (params.get("accessToken") == null) {
            return "";
        }
        String[] realToken = new String(Base64.getDecoder().decode(params.get("accessToken").toString())).split(":");
        if (realToken.length != 2) {
            return "";
        }
        String userId = realToken[1];
        if (isNullOrTrimEmpty(userId)) {
            return "";
        }
        return userId;
    }

    public static String getLoginUserId(Map params) {
        if (params.get("accessToken") == null) {
            throw new CustomException("没有登陆信息");
        }
        String[] realToken = new String(Base64.getDecoder().decode(params.get("accessToken").toString())).split(":");
        if (realToken.length != 2) {
            throw new CustomException("非法token");
        }

        String userId = realToken[1];
        if (isNullOrTrimEmpty(userId)) {
            throw new CustomException("非法token");
        }
        return userId;
    }


    public static List<Map> combineData(List<Map> result, CombineRule... combineRule) {
        if (result != null && !result.isEmpty()) {
            for (Map var1 : result) {
                for (CombineRule var2 : combineRule) {
                    //返回数据中查找的字段
                    Object targetField = var1.get(var2.getTargetField());
                    if (targetField != null) {
                        //目标数据
                        for (Map var3 : var2.getList()) {
                            //目标数据中对应的字段
                            Object var4 = var3.get(var2.getMappingField());
                            if (var4 != null) {
                                if (targetField.toString().equals(var4.toString())) {
                                    //映射数据
                                    for (Map<String, String> var5 : var2.getCombineField()) {
                                        var1.put(var5.get("value"), var3.get(var5.get("name")));
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }

            }
        }
        return result;
    }


    private static final String[] CHARS = new String[]{"a", "b", "c", "d", "e", "f",
            "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z"};

    /**
     * 短8位UUID思想其实借鉴微博短域名的生成方式，但是其重复概率过高，而且每次生成4个，需要随即选取一个。
     * 本算法利用62个可打印字符，通过随机生成32位UUID，由于UUID都为十六进制，所以将UUID分成8组，每4个为一组，然后通过模62操作，结果作为索引取出字符，
     * 这样重复率大大降低。
     * 经测试，在生成一千万个数据也没有出现重复，完全满足大部分需求。
     *
     * @return
     */
    public static String generateShortUuid() {
        StringBuilder shortBuffer = new StringBuilder();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        for (int i = 0; i < 8; i++) {
            String str = uuid.substring(i * 4, i * 4 + 4);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(CHARS[x % 0x3E]);
        }
        return shortBuffer.toString();

    }

    public static String getUuid() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 生成18位订单号
     *
     * @return
     */
    public static String generateOrderId() {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        return String.valueOf(idWorker.nextId());
    }

    /**
     * 获取客户端IP地址
     *
     * @return
     */
    public static String getClientIpAddress() {
        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token = req.getParameter("accessToken");
        return getClientIpAddress(req);
    }

    public static String getAccessToken() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String accessToken = request.getHeader("accessToken");
        if (accessToken == null || accessToken.isEmpty()) {
            throw new CustomException("accessToken不能为空", 100);
        }
        return accessToken;
    }


    /**
     * 获取客户端IP地址
     *
     * @param request
     * @return
     */
    public static String getClientIpAddress(HttpServletRequest request) {
        String ip = null;
        //X-Forwarded-For：Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP：apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP：weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP：有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP：nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }
        //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }
        //还是不能获取到，最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) || "::1".equals(ip) ? "127.0.0.1" : ip;
    }

    /**
     * 根据指定的字符串格式转成日期
     *
     * @param dateString
     * @param pattern
     * @return
     */
    public static Date pareDate(String dateString, String pattern) {
        try {
            if (HelperUtils.isNullOrTrimEmpty(pattern)) {
                pattern = "yyyy-MM-dd HH:mm:ss";
            }

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
            return simpleDateFormat.parse(dateString);
        } catch (Exception e) {
            return null;
        }
    }

    public static JSONObject toJsonObject(Object o) {
        return JSONObject.parseObject(JSONObject.toJSONString(o));
    }

    public static JSONArray toJsonArray(Object o) {
        return JSONObject.parseArray(JSONObject.toJSONString(o));
    }

    /**
     * 设置Excel导出格式
     *
     * @param response
     * @param fileName
     * @throws IOException
     */
    public static void setExcelResponse(HttpServletResponse response, String fileName) throws IOException {
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(), "iso-8859-1") + ".xlsx");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
    }

    /**
     * 判断对象中属性值是否全为空
     *
     * @param object
     * @return
     */
    public static boolean checkObjAllFieldsIsNull(Object object) {
        if (null == object) {
            return true;
        }
        try {
            for (Field f : object.getClass().getDeclaredFields()) {
                f.setAccessible(true);
                if (f.get(object) != null && StringUtils.isNotBlank(f.get(object).toString())) {
                    return false;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}
